package com.bsj.media.media;

import com.bsj.power.common.def.protocol.rtp.RtpParameterSet;
import com.bsj.power.common.util.Conv;
import lombok.extern.slf4j.Slf4j;


@Slf4j
public class FlvVideoStream extends mediaStream {


    public boolean isPacketFlvHeader; // 是否打包FLV头
    public int preTagSize; // 上一个tag大小
    public int videotimestamps; // 视频相对时间戳
    public int st_videoRate = 66;
    public int codeType; // 码流类型,0 不做判断，1是主码流，2是子码流，根据码流切换拼的地址来做
    public long lastTime; // 发生码流切换时，记录接收到数据的时间，如果超过3秒，就当做切换成功
    private long sendCount = 0; //打包包数计数
    private long sendCostTime = 0; //打包花费总时间

    public FlvVideoStream(String videoType, int codeType) {
        super.setVideoType(videoType);

        this.preTagSize = 0;
        this.videotimestamps = 0;
        this.isPacketFlvHeader = false;

        this.codeType = codeType;
        this.width = 0;
        this.height = 0;
    }

    /**
     * 打包FLV头信息
     *
     * @param body 原始时间
     * @param dst
     * @return
     */
    private int PacketFirstFLV(byte[] body, byte[] dst) {
        int mediaLen = 0; // 打包每帧数据长度
        int dstOffset = 0;

        mediaLen = ParserVideoData.packetFlvHeader(dst, dstOffset, 0, ParserVideoData.getVideoFrame(body, 0),
                getVideoType(), width, height, 0, 0, 0, ParserVideoData.getAudioType(body));
        // 前一个tag大小，减4是preTageSize存储字节数，减9是FLV头字节数
        preTagSize = mediaLen - 4 - 9;
        dstOffset += mediaLen;

        // 视频，打包第一个视频tag帧
        if (getVideoType() == 2) {
            // 这里打包关键帧里面的SPS，PPS数据
            mediaLen = ParserVideoData.PacketFlvData(body, dst, dstOffset, false, preTagSize);
            if (mediaLen <= 4) {
                log.warn("videoType: " + getVideoType() + " 打包头---sps,pps数据有错，丢掉---key:");
                return 0;
            }
            preTagSize = mediaLen - 4;

            // 改变buffer中时间戳，preTagSize:4, tag type:1, bodysize:3
            Conv.setIntNetOrder_3(dst, dstOffset + 8, videotimestamps);
            videotimestamps += st_videoRate;
            dstOffset += mediaLen;
        }

        isPacketFlvHeader = true;

        // 上面打包了 sps pps信息，在这边加入0x65(H264)或者19（H265） I 帧数据
        int srcOffset = 21;
        // 根据h264或者h265来选择打包的数据
        if (body[2] == RtpParameterSet.fdVideoCodeTypeH265) {
            mediaLen = ParserVideoData.packetH265IVideoData(body, srcOffset, dst, dstOffset, videotimestamps, 15, preTagSize);
        } else {
            // H264
            mediaLen = ParserVideoData.packetIVideoData(body, srcOffset, dst, dstOffset, videotimestamps, 15, preTagSize);
        }

        preTagSize = mediaLen - 4;
        videotimestamps += st_videoRate;
        dstOffset += mediaLen;

        return dstOffset;
    }

    /**
     * 打包FLV头后，打包其他FLV信息
     *
     * @param body
     * @param dst
     * @return
     */
    private int PacketSecondFLV(byte[] body, byte[] dst, int dstOffset) {
        if (!isPacketFlvHeader) {
            return -1;
        }

        int mediaLen = 0;
        int srcOffset = 1;// chn
        // 数据类型，1字节。0：视频I帧 1：视频P帧 2：视频B帧 3：音频帧 4：透传数据
        int dataType = Byte.toUnsignedInt(body[srcOffset]);
        // 编码格式 1-h264  3-h265
        int encodingFormat = Byte.toUnsignedInt(body[2]);
        // 偏移到h264数据
        srcOffset = 21;
        switch (dataType) {
            case 0: // 视频I帧
            {
                // 打包h264 关键帧中的SPS，PPS信息, 测试播放也会出现花屏，后来分辨率回复了，花屏结束
                // 打包视频I帧
                if (encodingFormat == RtpParameterSet.fdVideoCodeTypeH265) {
                    mediaLen = ParserVideoData.packetH265IVideoData(body, srcOffset, dst, dstOffset, videotimestamps, 15,
                            preTagSize);
                } else {
                    // H264
                    mediaLen = ParserVideoData.packetIVideoData(body, srcOffset, dst, dstOffset, videotimestamps, 15,
                            preTagSize);
                }
            }
            break;
            case 1:
            case 2:// 视频P，B帧
            {
                // 打包视频B，P帧
                mediaLen = ParserVideoData.packetBPVideoData(body, srcOffset, dst, dstOffset, videotimestamps, 15,
                        preTagSize);
            }
            break;
        }

        if (mediaLen > 0) {
            preTagSize = mediaLen - dstOffset - 4;
            videotimestamps += st_videoRate;
            dstOffset += mediaLen;
        }

        return dstOffset;
    }

    /**
     * 打包成flv，再发送
     */
    @Override
    public int PacketData(byte[] FdFdBody, int offset, int len, byte[] dstBuffer) {
        long t1 = System.currentTimeMillis();
        if (FdFdBody != null) {
            //包头(2字节)+主信令(2字节)+终端号长度(1字节)+终端号(10字节)+消息体长度(4字节)+消息体(n)+校验(1字节)+包尾(1字节)
            //消息体:通道(1) + 数据类型(1) + 编码格式(1) + 分辨率(1) + 码率类型(1) + 时间戳(8) + 多媒体id(8) + 数据体(n)
            // FDFD数据，第21字节表示音视频类型，3表示音频，0，1表示视频帧
            if (FdFdBody[20] == 0x3) {
                return 0;
            }

            int packetLen = 0;

            // 提取FDFD协议数据的数据体，19字节+body+校验1+结束位1
            byte[] body = super.getData(FdFdBody);

            if (!isPacketFlvHeader) {
                if (!ParserVideoData.checkIframe(body)) {
                    // 如果是只有音频的时候不判断这个
                    log.warn("打包头---不是I帧，丢掉---key:");
                    return 0;
                }

                // 获取视频分辨率
                int solutionRatio = ParserVideoData.getVideoSolution(body, 0);
                getSolutionRatio(solutionRatio);

                // 判断高标清切换数据是否有残余
                if (codeType == 1) {
                    if (lastTime == 0) {
                        lastTime = System.currentTimeMillis();
                    }
                    // 主码流 D1以上
                    if (solutionRatio < 3) {
                        if (System.currentTimeMillis() - lastTime < 3000) {
                            log.warn(" 是高清通道，码流分辨率为：" + width + ":" + height + " solutionRatio="
                                    + solutionRatio + " 不正确丢弃！");
                            return 0;
                        } else {
                            // 这时候认为切换成功：
                            log.info( "切换数据超过3秒，" + width + ":" + height + "solutionRatio=" + solutionRatio
                                    + "切换成功！");
                        }
                    }
                } else if (codeType == 2) {
                    if (lastTime == 0) {
                        lastTime = System.currentTimeMillis();
                    }
                    // 子码流D1以下
                    if (solutionRatio >= 3) {
                        if (System.currentTimeMillis() - lastTime < 3000) {
                            log.warn(" 是标清通道，码流分辨率为：" + width + ":" + height + " solutionRatio="
                                    + solutionRatio + " 不正确丢弃");
                            return 0;
                        } else {
                            // 这时候认为切换成功：
                            log.info("切换数据超过3秒，" + width + ":" + height + "solutionRatio=" + solutionRatio
                                    + "切换成功！");
                        }
                    }
                } else {
                    // == 0或其他不处理
                }

                // 算出时间间隔
                int videoRate = ParserVideoData.getVideoFrame(body, 0);
                if (videoRate > 0) {
                    st_videoRate = 1000 / videoRate;
                }
                // 打FLV头，h264信息
                packetLen = PacketFirstFLV(body, dstBuffer);
                // if (isPacketFlvHeader) {
                // packetLen = PacketSecondFLV(body, dstBuffer, packetLen);
                // }
            } else {
                packetLen = PacketSecondFLV(body, dstBuffer, 0);
            }

            return packetLen;
        }

        long t2 = System.currentTimeMillis();
        sendCostTime += (t2 - t1);
        sendCount++;
        if (sendCount % 450 == 1) {
            log.info("通道：" + " 总打包数：" + sendCount + ", 总耗时：" + sendCostTime + " ms, 评均耗时："
                    + (sendCostTime / sendCount) + "ms");
        }

        return 0;
    }
}
